use crate::trie::TrieMap;
use std::borrow::Borrow;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::ops::ControlFlow as 控制流;
use std::path::Path;
use 控制流::Continue as 繼續;

use None as 無;
use Option as 可選;
use Some as 有;

pub type 對照表 = TrieMap<String>;
pub type 引用對照表<'a> = TrieMap<&'a str>;

pub struct 配置參數 {
    pub 忽略行首: &'static str,
    pub 欄分隔符: &'static str,
    pub 對照項分隔符: 可選<&'static str>,
}

pub const 漢字對照表配置參數: 配置參數 = 配置參數 {
    忽略行首: "#",
    欄分隔符: "\t",
    對照項分隔符: 有(" "),
};

pub fn 讀取對照表(
    漢字對照表: impl AsRef<Path>,
    詞組對照表: impl AsRef<Path>,
    配置參數: 配置參數,
) -> std::io::Result<對照表> {
    let mut 對照表 = 對照表::new();
    for 行 in BufReader::new(File::open(漢字對照表.as_ref())?).lines() {
        let 行 = 行?;
        if 行.starts_with(配置參數.忽略行首) {
            continue;
        }
        let 兩欄: Vec<_> = 行.split(配置參數.欄分隔符).collect();
        let [漢字, 對照, ..] = &兩欄[..] else {
            continue;
        };
        let Some(漢字) = 漢字.chars().next() else {
            continue;
        };
        if let 有(分隔符) = 配置參數.對照項分隔符 {
            對照表.push_root_char_values(漢字, 對照.split(分隔符).map(ToString::to_string));
        } else {
            對照表.push_root_char_values(漢字, [對照.to_string()]);
        }
    }
    對照表.sort_root();
    for 行 in BufReader::new(File::open(詞組對照表)?).lines() {
        let 行 = 行?;
        if 行.starts_with('#') {
            continue;
        }
        let 兩項: Vec<_> = 行.split(配置參數.欄分隔符).collect();
        let [詞組, 對照] = &兩項[..] else {
            continue;
        };
        if let 有(分隔符) = 配置參數.對照項分隔符 {
            對照表.insert_all(詞組.chars(), 對照.split(分隔符).map(ToString::to_string));
        } else {
            對照表.insert_all(詞組.chars(), [對照.to_string()]);
        }
    }
    Ok(對照表)
}

pub fn 生成對照表<'a>(
    漢字對照表: &[(char, &[&'a str])],
    詞組對照表: &[(&str, &[&'a str])],
) -> 引用對照表<'a> {
    let mut 對照表 = 引用對照表::from_root_chars(
        漢字對照表
            .iter()
            .map(|&(key, values)| (key, values.iter().copied())),
    );
    對照表.extend(
        詞組對照表
            .iter()
            .map(|&(key, values)| (key.chars(), values.iter().copied())),
    );
    對照表
}

pub fn 轉換<T: Borrow<str> + std::fmt::Debug>(輸入: &str, 對照表: &TrieMap<T>) -> String {
    let mut 輸出 = String::new();
    轉換器(輸入, 對照表, |字詞| {
        輸出.push_str(字詞.unwrap_or_else(std::convert::identity));
        繼續(())
    });
    輸出
}

pub fn 轉換器<T: Borrow<str> + std::fmt::Debug>(
    mut 輸入: &str,
    對照表: &TrieMap<T>,
    mut 拼接器: impl FnMut(Result<&str, &str>) -> 控制流<()>,
) {
    while !輸入.is_empty() {
        match 對照表.get(輸入) {
            有(([字詞, ..], 餘文)) => {
                if 拼接器(Ok(字詞.borrow())).is_break() {
                    break;
                }
                輸入 = 餘文;
            }
            有(([], _)) | 無 => {
                let mut 輸入字串 = 輸入.char_indices();
                let Some(_) = 輸入字串.next() else { break };
                let 偏移量 =
                    unsafe { 輸入字串.as_str().as_ptr().offset_from(輸入.as_ptr()) as usize };
                if 拼接器(Err(&輸入[..偏移量])).is_break() {
                    break;
                }
                輸入 = 輸入字串.as_str();
            }
        }
    }
}

#[cfg(test)]
mod 測試 {
    use super::*;

    fn 簡繁對照表() -> 引用對照表<'static> {
        生成對照表(
            &[('干', &["干", "乾", "幹"]), ('扰', &["擾"])],
            &[("干扰", &["干擾"]), ("干脆", &["乾脆"])],
        )
    }

    #[test]
    fn 測試簡轉繁() {
        let 對照表 = 簡繁對照表();
        assert_eq!(轉換("扰", &對照表), "擾");
        assert_eq!(轉換("干扰", &對照表), "干擾");
        assert_eq!(轉換("干脆", &對照表), "乾脆");
        assert_eq!(轉換("那我干脆不做了", &對照表), "那我乾脆不做了");
    }
}
